package com.lincoln.service;

import com.lincoln.entity.base.BaseEntity;
import com.lincoln.repository.base.BaseDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public abstract class BaseService<ENTITY extends BaseEntity, REPOSITORY extends BaseDao<ENTITY>> {
    @Autowired
    public REPOSITORY repository;

    public PageRequest buildPageRequest(int pageNo, int pageSize, Sort.Order... orders) {
        Sort sort = Sort.by(orders);
        return PageRequest.of(pageNo, pageSize, sort);
    }

    public Predicate createPredicate(Root<ENTITY> root, CriteriaBuilder cb, Map<String, Object> params) {
        Predicate result = cb.and();
        for (String key : params.keySet()) {
            if (params.get(key) != null) {
                Predicate p = cb.equal(root.get(key).as(params.get(key).getClass()), params.get(key));
                result = cb.and(result, p);
            }
        }
        return result;
    }

    public Page<ENTITY> findPage(PageRequest pageRequest, Map<String, Object> params) {
        Specification<ENTITY> specification = new Specification<ENTITY>() {
            @Override
            public Predicate toPredicate(Root<ENTITY> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return createPredicate(root, cb, params);
            }
        };
        Page<ENTITY> page = repository.findAll(specification, pageRequest);
        return page;
    }

    public Page<ENTITY> findPage(PageRequest pageRequest, Specification specification) {
        Page<ENTITY> page = repository.findAll(specification, pageRequest);
        return page;
    }

    public List<ENTITY> findList(Specification specification) {
        List<ENTITY> list = repository.findAll(specification);
        return list;
    }

    public List<ENTITY> findList(Map<String, Object> params) {
        Specification<ENTITY> specification = new Specification<ENTITY>() {
            @Override
            public Predicate toPredicate(Root<ENTITY> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return createPredicate(root, cb, params);
            }
        };
        List<ENTITY> list = repository.findAll(specification);
        return list;
    }

    /**
     * 通过键值对查找
     *
     * @param key
     * @param value
     * @return
     */
    public List<ENTITY> findByUnique(String key, Object value) {
        Specification<ENTITY> specification = new Specification<ENTITY>() {
            @Override
            public Predicate toPredicate(Root<ENTITY> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate result = cb.and();
                Predicate p = cb.equal(root.get(key).as(value.getClass()), value);
                result = cb.and(result, p);
                return result;
            }
        };
        List<ENTITY> list = repository.findAll(specification);
        return list;
    }

    /**
     * 通过键值对查找,并取出第一个值(用于非数据库主键的业务逻辑主键,比如用户名)
     *
     * @param key
     * @param value
     * @return
     */
    public ENTITY findByUniqueForOne(String key, Object value) {
        List<ENTITY> list = findByUnique(key, value);
        if (list == null || list.size() <= 0) {
            return null;
        } else {
            return list.get(0);
        }
    }

    public ENTITY save(ENTITY entity) {
        if (null == entity.getCreateTime()) {
            entity.setCreateTime(new Date());
            entity.setUpdateTime(new Date());
        } else {
            entity.setUpdateTime(new Date());
        }
        return repository.save(entity);
    }

    public void delete(ENTITY entity) {
        repository.delete(entity);
    }

    public ENTITY findById(long id) {
        try {
            return repository.findById(id).get();
        } catch (Exception e) {
            return null;
        }
    }

    public List<ENTITY> findAll() {
        return repository.findAll();
    }
}
